"""
@File    :   in.py
@Time    :   2024/03/25 08:35:17
@Author  :   Qrj
@Email   :   923207736@qq.com
@description   :   全局的pydantic验证模型
"""

from functools import lru_cache
from typing import Any, Literal, Type

from pydantic import BaseModel, ConfigDict, Field
from sqlalchemy import and_

from src.config import get_app_settings
from src.models.models import OrmBase

# 获取应用配置
settings = get_app_settings()


class BaseValidateSchemaConfigMixin:
    """全局Pydantic验证模型的配置混合类"""

    model_config = ConfigDict(
        str_strip_whitespace=True,  # 去除str类型的前后空格(默认False)
        populate_by_name=True,  # 是否允许使用别名进行实例化(默认False)
        use_enum_values=True,  # 直接将枚举类型转为value
    )


class NoUseEnumValuesBaseValidateSchemaConfigMixin:
    """不直接将枚举类型转为value的全局Pydantic验证模型的配置混合类"""

    model_config = ConfigDict(
        str_strip_whitespace=True,  # 去除str类型的前后空格(默认False)
        populate_by_name=True,  # 是否允许使用别名进行实例化(默认False)
    )


class BaseSearchValidateSchemaMixin:
    """全局的Pydantic查询验证模型的混合类"""

    model_config = ConfigDict(
        str_strip_whitespace=True,  # 去除str类型的前后空格(默认False)
        populate_by_name=True,  # 是否允许使用别名进行实例化(默认False)
        use_enum_values=True,  # 直接将枚举类型转为value
    )

    @classmethod
    @lru_cache
    def get_schema_field_obj_mappings(cls: Type[BaseModel]) -> dict[str, Any]:
        """获取pydantic模型的字段名称与其字段类型实例的映射"""
        return cls.model_fields

    def convert_to_where_sql(self: BaseModel, sql_model: Type[OrmBase]) -> list[Any]:
        """pydantic查询模型实例转化为SQL的where语句
        关于Field的json_schema_extra参数, 它是一个dict, 目前各参数作用为:
            {
                "str_like_search_type": "prefix/suffix/complete",    # 字符串模糊查询的类型(左前缀、后缀、完全模糊检索)
                "allow_order": True,    # 该字段是否允许排序(主键ID字段默认允许排序)
            }

        """
        # 获取查询的参数与字段的映射(排除为None的字段)
        search_value_mappings = self.model_dump(exclude_none=True)
        # 获取pydantic模型的字段名称与其字段类型实例的映射
        search_schema_field_obj_mappings = self.get_schema_field_obj_mappings()  # type: ignore
        # 构建sql的where语句参数
        result_sql = []
        for field, value in search_value_mappings.items():
            # 当前字段是否已构建where语句参数
            structed = False
            if isinstance(value, tuple):
                value_len = len(value)
                if value_len == 2:
                    # 长度为2的tuple类型表示一个区间
                    # 排序后进行区间查找
                    sorted_value = sorted(value)
                    result_sql.append(
                        and_(
                            getattr(sql_model, field) >= sorted_value[0],
                            getattr(sql_model, field) <= sorted_value[1],
                        )
                    )
                    structed = True
                elif value_len > 2:
                    # 长度大于2则当做list处理
                    result_sql.append(getattr(sql_model, field).in_(value))
                    structed = True
                elif value_len == 1:
                    # 如果tuple的长度为1, 则当做普通的value处理(向下漏)
                    structed = False
                else:
                    # 长度为0则不做处理
                    structed = True
            elif isinstance(value, list):
                value_len = len(value)
                if value_len >= 2:
                    # 为list则使用in查询
                    # 这里条件是一个明确的集合, 非子查询, 所以可以使用in和not_in
                    # 这种情况下 in 比一个个or要快的多
                    result_sql.append(getattr(sql_model, field).in_(value))
                    structed = True
                elif value_len == 1:
                    # 如果list的长度为1, 则当做普通的value处理
                    structed = False
                else:
                    # 长度为0则不做处理
                    structed = True
            # 处理其他类型的字段
            if not structed:
                # 获取字段类型实例
                field_obj = search_schema_field_obj_mappings[field]
                # 获取字段类型实例的拓展信息
                field_json_schema_extra = getattr(field_obj, "json_schema_extra", None)
                if isinstance(value, str):
                    # 字符串类型
                    if field_json_schema_extra:
                        # 当有找到字段类型的拓展信息时, 说明有特殊的where构建方式
                        # 获取"字符串模糊查询的类型"
                        str_like_search_type = field_json_schema_extra.get(
                            "str_like_search_type", None
                        )
                        # 根据模糊查询的类型构建where sql
                        if str_like_search_type == "prefix":
                            # 左前缀模糊查询, 只有这种能走索引
                            search_like_str = value + "%"
                        elif str_like_search_type == "suffix":
                            # 后缀模糊查询
                            search_like_str = "%" + value
                        else:
                            # 完全模糊查询
                            search_like_str = "%" + value + "%"
                        result_sql.append(
                            getattr(sql_model, field).like(search_like_str)
                        )
                    else:
                        # 没有拓展信息时, 直接用==
                        result_sql.append(getattr(sql_model, field) == value)
                else:
                    # 其他类型字段都暂时全部用 ==
                    result_sql.append(getattr(sql_model, field) == value)
        return result_sql


class PaginationSearchValidateSchema(BaseModel, BaseValidateSchemaConfigMixin):
    """全局的分页查询Pydantic验证模型"""

    page: int = Field(default=1, ge=1, le=99999999, description="页数")
    size: int = Field(
        default=settings.default_pagination_size,
        ge=1,
        le=settings.default_max_pagination_size,
        description="分页数据量",
    )

    @property
    def offset(self) -> int:
        """sql所需的offset参数"""
        return (self.page - 1) * self.size

    @property
    def limit(self) -> int:
        """sql所需的limit参数"""
        return self.size


class OrderBySearchValidateSchema(BaseModel, BaseValidateSchemaConfigMixin):
    """全局的查询排序Pydantic验证模型"""

    order_field: str | None = Field(
        default=None, min_length=1, max_length=999, description="排序字段"
    )
    order_type: Literal["asc", "desc"] = Field(
        default="asc", description="排序方式"
    )  # asc升序, desc降序

    def convert_to_order_by_sql(
        self, search_schema: BaseModel | Type[BaseModel], sql_model: OrmBase
    ) -> Any:
        """转化为SQL的order_by语句参数
        可进行排序的字段要求:
            1. 将search_schema模型内字段的json_schema_extra的allow_order键值设置为True
            2. 满足第一点条件的字段在sql_model中存在
            3. 满足第一二点条件的字段可设置为order_field
            4. 不满足条件统一使用主键ID作为order_field

        Args:
            search_schema (BaseModel | Type[BaseModel]): pydantic查询模型
            sql_model (OrmBase): sql模型

        Returns:
            Any: SQL的order_by语句参数
        """
        # 获取默认的排序字段
        default_order_field = (
            getattr(sql_model, "show_order")
            if getattr(sql_model, "show_order", None) is not None
            else getattr(sql_model, "id")
        )
        # 构建默认的排序
        default_order = getattr(default_order_field, self.order_type)()
        # 获取pydantic模型的字段名称与其字段类型实例的映射
        search_schema_field_obj_mappings = search_schema.get_schema_field_obj_mappings()  # type: ignore
        # 从上面的映射中获取排序字段对应的pydantic查询模型字段的实例
        search_schema__order_by_field = search_schema_field_obj_mappings.get(
            self.order_field, None
        )
        if search_schema__order_by_field is None:
            # 没有在pydantic查询模型中找到对应的排序字段时, 使用默认的排序
            return default_order
        else:
            # 在pydantic查询模型中找到对应的排序字段时
            # 检查是否有注明json_schema_extra
            field_json_schema_extra = getattr(
                search_schema__order_by_field, "json_schema_extra", None
            )
            if field_json_schema_extra is None or (
                not field_json_schema_extra.get("allow_order", False)
            ):
                # 没有注明json_schema_extra
                # 或json_schema_extra的allow_order键值为False时, 使用默认的排序
                return default_order
            else:
                # 条件都满足, 则使用指定的条件进行排序
                return getattr(getattr(sql_model, self.order_field), self.order_type)()
